package com.spukmk3me.resource;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

/* $if SPUKMK3ME_DEBUG$ */
import com.spukmk3me.debug.Logger;
/* $endif$ */
import com.spukmk3me.io.FileHandle;
import com.spukmk3me.util.DoublyLinkedList;

public final class ResourceManager
{
    public ResourceManager()
    {
        m_creators = new DoublyLinkedList();
        m_resources = new DoublyLinkedList();
    }
    
    public void addResourceLoader( ResourceCreator creator )
    {
        if ( creator == null )
        {
            /* $if SPUKMK3ME_DEBUG$ */
            Logger.trace( "Resource loader cannot be null." );
            /* $endif$ */
            return;
        }
        
        /* $if SPUKMK3ME_DEBUG$ */
        DoublyLinkedList.Iterator i = m_creators.first();
        DoublyLinkedList.Iterator e = m_creators.end();
        String[] loadableList = creator.getSupportedTypes();
        String[] temp;
        
        for ( ; !i.equals( e ); i.fwrd() )
        {
            temp = ((ResourceCreator)i.data()).getSupportedTypes();
            
            for ( int j = 0; j != loadableList.length; ++j )
            {
                for ( int k = 0; k != temp.length; ++k )
                {
                    if ( loadableList[ j ].equals( temp[ k ] ) )
                       Logger.trace( "Warning: Duplicated loadable resource type " + temp[ k ] );
                }
            }
        }
        /* $endif$ */
        
        m_creators.push_back( creator );
    }
    
    public boolean removeResourceLoader( ResourceCreator loader )
    {
        return m_creators.erase( loader );
    }
    
    /**
     *  Add new resource to the resource manager.
     *  @param handle
     *  @param type
     *  @param creationInfo
     *  @return New resource or the existing resource with the same type and same handle.
     */
    public Resource addResource( FileHandle handle, String type )
    {
        /* $if SPUKMK3ME_DEBUG$ */
        Logger.log( "Loading resource type: " + type + ", " + handle + "... " );
        /* $endif$ */
        
        // Find the existing Resource
        DoublyLinkedList.Iterator i = m_resources.first();
        DoublyLinkedList.Iterator e = m_resources.end();
        Resource res = null;
        
        for ( ; !i.equals( e ); i.fwrd() )
        {
            res = (Resource)i.data();
            
            if ( handle == res.getFileHandle() )
            {
                if ( type.equals( res.getType() ) )
                {
                    /* $if SPUKMK3ME_DEBUG$ */
                    Logger.log( "found in cache.\n" );
                    /* $endif$ */
                    
                    return res;
                }
            }
        }
        
        /* $if SPUKMK3ME_DEBUG$ */
        Logger.log( "trying to assign..." );
        /* $endif$ */
        
        // Resource not exists, search for properiate loader
        i = m_creators.first();
        e = m_creators.end();
        ResourceCreator loader;
        
        for ( ; !i.equals( e ); i.fwrd() )
        {
            loader = (ResourceCreator)i.data();
            
            if ( isSupported( loader, type ) )
            {
                res = loader.createResource( handle, type );
                
                if ( res != null )
                {
                    res.inc();
                    res.assign( handle, type );
                    m_resources.push_back( res );
                    /* $if SPUKMK3ME_DEBUG$ */
                    Logger.log( "assigned.\n" );
                    /* $endif$ */
                    return res;
                }
            }
        }
        
        /* $if SPUKMK3ME_DEBUG$ */
        Logger.log( "resource not found.\n" );
        /* $endif$ */
        return null;
    }
    
    /**
     *  Add and load the resource at the same time.
     *  @param handle
     *  @param type
     *  @param creationInfo
     *  @return 
     *  @throws IOException
     */
    public Resource loadResource( FileHandle handle, String type ) throws IOException
    {
        Resource res = addResource( handle, type );
        
        loadResource( res );
        return res;
    }
    
    /**
     *  Load an added resource.    
     *  @param resource
     *  @throws IOException
     */
    public void loadResource( Resource resource ) throws IOException
    {
        if ( resource.getState() == Resource.ASSIGNED )
            resource.load();
    }
    
    /**
     *  Load all resources with ASSIGNED state.
     *  @throws IOException
     */
    public void loadAllResources() throws IOException
    {
        DoublyLinkedList.Iterator i = m_resources.first();
        DoublyLinkedList.Iterator e = m_resources.end();
        Resource res;
        
        for ( ; !i.equals( e ); i.fwrd() )
        {
            res = (Resource)i.data();
            
            if ( res.getState() == Resource.ASSIGNED )
                res.load();
        }
    }

    /**
     *  Release a loaded resource, force it to ASSIGNED state.
     *  @param resource
     */
    public void releaseResource( Resource resource )
    {
        resource.release();
    }
    
    /**
     *  Remove all unused resources (resources aren't referenced by any Node)
     * from managed list and release them.
     */
    public void clearAllUnusedResource()
    {
        DoublyLinkedList.Iterator i = m_resources.first();
        DoublyLinkedList.Iterator e = m_resources.end();
        Resource res;
        
        for ( ; !i.equals( e ); )
        {
            res = (Resource)i.data();
            //System.out.println( "Counter: " + res.getCounter() );
            
            if ( res.getCounter() == 1 )
            {
                i.fwrd();
                m_resources.erase( res );
                res.dec();
            }
            else
                i.fwrd();
        }
    }
    
    public List<Resource> getResources( String type )
    {
        List<Resource> resList = new LinkedList<Resource>();
        DoublyLinkedList.Iterator i = m_resources.first();
        DoublyLinkedList.Iterator e = m_resources.end();
        Resource res;
        
        while ( !i.equals( e ) )
        {
            res = (Resource)i.data();
            
            if ( res.getType().equals( type ) )
                resList.add( res );
            
            i.fwrd();
        }
        
        
        return resList;
    }
    
    private static boolean isSupported( ResourceCreator creator, String resourceType )
    {
        String[] loadableList = creator.getSupportedTypes();
        
        for ( int i = 0; i != loadableList.length; ++i )
        {
            if ( resourceType.equals( loadableList[ i ] ) )
                return true;
        }
        
        return false;
    }
    
    private DoublyLinkedList m_creators;
    private DoublyLinkedList m_resources;
}
